package com.bodsite.rpc.thrift.client;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Map;

import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.thrift.TApplicationException;
import org.apache.thrift.TServiceClient;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import com.bodsite.common.logger.Logger;
import com.bodsite.common.logger.LoggerFactory;
import com.bodsite.rpc.spring.schema.bean.Invok;
import com.bodsite.rpc.spring.schema.bean.Module;
import com.bodsite.rpc.spring.schema.bean.Zookeeper;
import com.bodsite.rpc.thrift.client.pool.ThriftClientPoolFactory;
import com.bodsite.rpc.thrift.constant.ThriftConstant;
import com.bodsite.rpc.thrift.expetion.RpcException;
import com.bodsite.rpc.thrift.expetion.RpcException.RPC_EXPECTION;

public class ProxyService<T> implements FactoryBean<T>, InitializingBean, ApplicationContextAware {

	private static Logger logger = LoggerFactory.getLogger(ProxyService.class);
	
	private T iface;
	private Class<?> ifaceClass;
	private String id;
	private Module module;
	private Zookeeper zookeeper;
	private Invok invok;
	private GenericObjectPool<TServiceClient> serviceClientPool;
	private ApplicationContext applicationContext;

	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		this.applicationContext = applicationContext;
	}

	@Override
	public T getObject() throws Exception {
		return iface;
	}

	private Object rpc(Method method, Object[] args)
			throws Exception{
		TServiceClient client = null;
		try {
			client = serviceClientPool.borrowObject();
		} catch (Exception e) {
			throw new RpcException(RPC_EXPECTION.RPC_INVOK_EXCEPTION, e,
					e.getMessage() + " : " + id + "." + method.getName() + "(" + args + ")");
		}
		try {
			Object result = method.invoke(client, args);
			try {
				serviceClientPool.returnObject(client);
			} catch (Exception e) {
				logger.error(e.getMessage(), e);
			}
			return result;
		} catch (InvocationTargetException e) {
			serviceClientPool.invalidateObject(client);
			throw e;
		}
	}

	@Override
	public Class<?> getObjectType() {
		return ifaceClass;
	}

	@Override
	public boolean isSingleton() {
		return true;
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		build();
		ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
		iface = (T) Proxy.newProxyInstance(classLoader, new Class[] { ifaceClass }, new InvocationHandler() {
			@Override
			public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
				// TODO Auto-generated method stub
				return rpc(method, args);
			}
		});
	}

	private void build() throws Exception {
		module = applicationContext.getBean(Module.class);
		zookeeper = applicationContext.getBean(Zookeeper.class);
		Map<String, Invok> invokMap = applicationContext.getBeansOfType(Invok.class);
		invok = invokMap.get(id);// 获取调用类
		ServiceDiscovery serviceDiscovery = new ServiceDiscovery(getServicePath(zookeeper, module), zookeeper);
		ThriftClientPoolFactory t = new ThriftClientPoolFactory(invok, serviceDiscovery);
		serviceClientPool = t.getTServiceClientPool();
	}

	private String getServicePath(Zookeeper zookeeper, Module module) {
		return ThriftConstant.SERVICE_HOME + "/" + zookeeper.getGroup() + "/" + module.getVersion() + "/" + id;
	}

	public String getId() {
		return id;
	}

	public void setId(String id) {
		this.id = id;
	}

	public Class<?> getIfaceClass() {
		return ifaceClass;
	}

	public void setIfaceClass(Class<?> ifaceClass) {
		this.ifaceClass = ifaceClass;
	}

}
